﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace SemanticEx
{
	public static class ExpressionEx
	{
		/// <summary>
		/// Ensure that some sort of object type is returned by the expression.
		/// </summary>
		public static Expression EnsureObjectResult(Expression expr)
		{
			if (!expr.Type.IsValueType)
			{
				return expr;
			}
			if (expr.Type == typeof(void))
			{
				return Expression.Block(expr, Expression.Default(typeof(object)));
			}
			else
			{
				return Expression.Convert(expr, typeof(object));
			}
		}

		public static DynamicExpression MakeBinary(ExpressionType type, Expression left, Expression right)
		{
			return Expression.Dynamic(new SimpleBinaryOperationBinder(type), typeof(object), left, right);
		}

		public static DynamicExpression MakeUnary(ExpressionType type, Expression expr)
		{
			return Expression.Dynamic(new SimpleUnaryOperationBinder(type), typeof(object), expr);
		}

		public static DynamicExpression And(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.And, left, right);
		}

		public static DynamicExpression Or(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.Or, left, right);
		}

		public static DynamicExpression Equal(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.Equal, left, right);
		}

		public static DynamicExpression NotEqual(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.NotEqual, left, right);
		}

		public static DynamicExpression GreaterThanOrEqual(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.GreaterThanOrEqual, left, right);
		}

		public static DynamicExpression LessThanOrEqual(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.LessThanOrEqual, left, right);
		}

		public static DynamicExpression LessThan(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.LessThan, left, right);
		}

		public static DynamicExpression GreaterThan(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.GreaterThan, left, right);
		}

		public static DynamicExpression Add(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.Add, left, right);
		}

		public static DynamicExpression Subtract(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.Subtract, left, right);
		}

		public static DynamicExpression Multiply(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.Multiply, left, right);
		}

		public static DynamicExpression Divide(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.Divide, left, right);
		}

		public static DynamicExpression Power(Expression left, Expression right)
		{
			return MakeBinary(ExpressionType.Power, left, right);
		}

		public static DynamicExpression Not(Expression expr)
		{
			return MakeUnary(ExpressionType.Not, expr);
		}

		public static DynamicExpression Negate(Expression expr)
		{
			return MakeUnary(ExpressionType.Negate, expr);
		}

		public static MethodCallExpression Call(Type type, string methodName, params Expression[] arguments)
		{
			return Expression.Call(type.GetMethod(methodName, arguments.Select(x => x.Type).ToArray()), arguments);
		}

		public static MethodCallExpression Call<T>(string methodName, params Expression[] arguments)
		{
			return Expression.Call(typeof(T).GetMethod(methodName, arguments.Select(x => x.Type).ToArray()), arguments);
		}
	}
}